home *** CD-ROM | disk | FTP | other *** search
/ ETO Development Tools 2 / ETO Development Tools 2.iso / Tools - Objects / MacApp / MacApp CD Release / MacApp 2.0.1 (Many Libraries) / Libraries / UList.inc1.p < prev    next >
Encoding:
Text File  |  1990-10-25  |  48.5 KB  |  1,865 lines  |  [TEXT/MPS ]

  1. {$P}
  2. {[a-,body+,h-,o=100,r+,rec+,t=4,u+,#+,j=20/57/1$,n-]}
  3. { UList.inc1.p }
  4. { Copyright © 1986-1990 by Apple Computer, Inc.  All rights reserved. }
  5.  
  6. {$IFC NOT qDebugTheDebugger}                            { Lists are core to MacApp. They really need
  7.                                                          to be at least tolerable in the Debug
  8.                                                          environment. }
  9. {$W+}
  10. {$Init-}
  11. {$OV-}
  12. {$ENDC}
  13. {$IFC qNames}
  14. {$D+}
  15. {$ENDC}
  16.  
  17. {--------------------------------------------------------------------------------------------------}
  18. {$S ListRes}
  19.  
  20. PROCEDURE TPtrBasedDoublyLinkedList.IPtrBasedDoublyLinkedList;
  21.  
  22.     BEGIN
  23.     IObject;
  24.  
  25.     fHeadNodePtr := NIL;
  26.     fTailNodePtr := NIL;
  27.     END;
  28.  
  29. {--------------------------------------------------------------------------------------------------}
  30. {$S ListRes}
  31.  
  32. PROCEDURE TPtrBasedDoublyLinkedList.AppendNode(thisNode: UNIV PtrBasedDoublyLinkedListNodePtr);
  33.  
  34.     BEGIN
  35.     { if a chain exists then link me in }
  36.     IF fTailNodePtr <> NIL THEN
  37.         BEGIN
  38.         thisNode^.previousLink := fTailNodePtr;         { set my back link }
  39.         fTailNodePtr^.nextLink := thisNode;             { set the old tail's forward link }
  40.         thisNode^.nextLink := NIL;                        { I'm last }
  41.  
  42.         fTailNodePtr := thisNode;
  43.         END
  44.     ELSE                                                { start a new chain }
  45.         BEGIN
  46.         thisNode^.previousLink := NIL;                    { I'm first }
  47.         thisNode^.nextLink := NIL;                        { I'm last }
  48.  
  49.         fHeadNodePtr := thisNode;
  50.         fTailNodePtr := thisNode;
  51.         END;
  52.  
  53.     END;
  54.  
  55. {--------------------------------------------------------------------------------------------------}
  56. {$S ListRes}
  57.  
  58. PROCEDURE TPtrBasedDoublyLinkedList.RemoveNode(thisNode: UNIV PtrBasedDoublyLinkedListNodePtr);
  59.  
  60.     BEGIN
  61.     { remove me from the list ends }
  62.     IF fHeadNodePtr = thisNode THEN
  63.         fHeadNodePtr := thisNode^.nextLink;
  64.     IF fTailNodePtr = thisNode THEN
  65.         fTailNodePtr := thisNode^.previousLink;
  66.  
  67.     { remove me from the chain }
  68.     IF thisNode^.nextLink <> NIL THEN
  69.         thisNode^.nextLink^.previousLink := thisNode^.previousLink;
  70.     IF thisNode^.previousLink <> NIL THEN
  71.         thisNode^.previousLink^.nextLink := thisNode^.nextLink;
  72.  
  73.     END;
  74.  
  75. {--------------------------------------------------------------------------------------------------}
  76. {$S ListRes}
  77.  
  78. PROCEDURE TPtrBasedDoublyLinkedList.EachNodeDo(PROCEDURE
  79.                                                DoToNode(thisNode: UNIV
  80.                                                         PtrBasedDoublyLinkedListNodePtr));
  81.  
  82.     VAR
  83.         aNodePtr:            PtrBasedDoublyLinkedListNodePtr;
  84.  
  85.     BEGIN
  86.     aNodePtr := fTailNodePtr;
  87.     WHILE (aNodePtr <> NIL) DO
  88.         BEGIN
  89.         DoToNode(aNodePtr);
  90.         aNodePtr := aNodePtr^.previousLink;
  91.         END;
  92.     END;
  93.  
  94. {--------------------------------------------------------------------------------------------------}
  95. {$S MAFields}
  96.  
  97. PROCEDURE TPtrBasedDoublyLinkedList.Fields(PROCEDURE
  98.                                            DoToField(fieldName: Str255;
  99.                                                      fieldAddr: Ptr;
  100.                                                      fieldType: integer)); OVERRIDE;
  101.  
  102.     BEGIN
  103.     DoToField('TPtrBasedDoublyLinkedList', NIL, bClass);
  104.     DoToField('fHeadNodePtr', @fHeadNodePtr, bPointer);
  105.     DoToField('fTailNodePtr', @fHeadNodePtr, bPointer);
  106.  
  107.     INHERITED Fields(DoToField);
  108.     END;
  109.  
  110. {--------------------------------------------------------------------------------------------------}
  111. {$S ListRes}
  112.  
  113. PROCEDURE TDynamicArray.IDynamicArray(initialSize: ArrayIndex;
  114.                                       elementSize: integer);
  115.  
  116.     BEGIN
  117.     fFreeRequested := FALSE;                            { no comment can do this justice }
  118.  
  119.     IPtrBasedDoublyLinkedList;
  120.     
  121.     fClassSize := GetClassSize;                            { Store the class size for use in
  122.                                                         computeAddress }
  123.  
  124.     IF qDebug & (initialSize < kEmptyIndex) THEN
  125.         BEGIN
  126.         ProgramBreak('initialSize must be non-negative!');
  127.         initialSize := kEmptyIndex;                     {??? Ask programmer }
  128.         END;
  129.  
  130.     IF qDebug & (elementSize <= 0) THEN
  131.         BEGIN
  132.         ProgramBreak('In TDynamicArray.IDynamicArray: preposterous element size. (zero or negative)'
  133.                      );
  134.         Failure(minErr, 0);
  135.         END;
  136.  
  137.     fSize := kEmptyIndex;
  138.  
  139.     fElementSize := elementSize;
  140.     fAllocatedSize := kEmptyIndex;
  141.  
  142.     fAllocationIncrement := kAllocationIncrement;        { !!! an enhancement would be to support
  143.                                                          no-growth lists. i.e. fAllocationIncrement
  144.                                                          of kEmptyIndex. the list would just stay
  145.                                                          at the size it was allocated and wouldn't
  146.                                                          shrink and signal failure if an operation
  147.                                                          needed to grow it }
  148.  
  149.     { calculate the elementsizeshift that represents the nearest power of 2 }
  150.     fElementSizeShift := 0;
  151.     WHILE BSR(elementSize - 1, fElementSizeShift) > 0 DO
  152.         fElementSizeShift := fElementSizeShift + 1;
  153.  
  154.     
  155.     SetArraySize(initialSize);
  156.  
  157.     END;
  158.  
  159. {--------------------------------------------------------------------------------------------------}
  160. {$S ListRes}
  161.  
  162. PROCEDURE TDynamicArray.DeleteElementsAt(index: ArrayIndex;
  163.                                          count: ArrayIndex);
  164.  
  165.     CONST
  166.         initVal             = $F1;
  167.  
  168.     VAR
  169.         indexPtr, nextElementPtr, lastElementPtr: Ptr;
  170.         countBytes:         ArrayIndex;
  171.  
  172.     PROCEDURE DoToNode(thisNode: UNIV IterationNodePtr); { bends the index in the (usually local to
  173.                                                           the Iterator) Iteration node to account
  174.                                                           for the deleted elements }
  175.  
  176.         BEGIN
  177.         WITH thisNode^ DO
  178.             BEGIN
  179.             IF iterForward THEN
  180.                 BEGIN
  181.                 { If the deleted element was NOT in the
  182.                 range yet to be iterated then bend the
  183.                 iterIndex to account for it. }
  184.                 IF index < iterLowBound THEN
  185.                     iterLowBound := iterLowBound - count;
  186.  
  187.                 IF index <= iterIndex THEN
  188.                     iterIndex := iterIndex - count;
  189.  
  190.                 IF index <= iterHighBound THEN
  191.                     iterHighBound := iterHighBound - count;
  192.                 END
  193.             ELSE                                        { Iterating backwards }
  194.                 BEGIN
  195.                 { If the deleted element was IN the range
  196.                 yet to be iterated then bend the iterIndex
  197.                 to account for it. }
  198.                 IF index < iterLowBound THEN
  199.                     iterLowBound := iterLowBound - count;
  200.  
  201.                 IF index < iterIndex THEN
  202.                     iterIndex := iterIndex - count;
  203.  
  204.                 IF index <= iterHighBound THEN
  205.                     iterHighBound := iterHighBound - count;
  206.                 END;
  207.             END;
  208.         END;
  209.  
  210.     BEGIN
  211.     IF qRangeCheck & ((index < 1) | (index > fSize)) THEN
  212.         BEGIN
  213.         Writeln('fSize = ', fSize: 1, '  index = ', index: 1);
  214.         ProgramBreak('Range Check in TDynamicArray.DeleteElementAt');
  215.         END;
  216.  
  217.     countBytes := BSL(count, fElementSizeShift);
  218.  
  219.     indexPtr := ComputeAddress(index);
  220.     nextElementPtr := ComputeAddress(index + count);
  221.     lastElementPtr := ComputeAddress(fSize + 1);
  222.  
  223.     IF ord(nextElementPtr) < ord(lastElementPtr) THEN    { deleted from middle? Compress the array }
  224.         BlockMove(nextElementPtr, indexPtr, ord(lastElementPtr) - ord(nextElementPtr));
  225.  
  226.     {$Push} {$R-}
  227.     IF qDebug THEN
  228.         BlockSet(Ptr(ord(lastElementPtr) - countBytes), countBytes, initVal);
  229.     {$Pop}
  230.  
  231.     SetArraySize(fSize - count);                        { take up slack if necessary. Should never
  232.                                                          Fail when shrinking array. }
  233.  
  234.     fSize := fSize - count;
  235.  
  236.     IF fTailNodePtr <> NIL THEN
  237.         EachNodeDo(DoToNode);
  238.     END;
  239.  
  240. {--------------------------------------------------------------------------------------------------}
  241. {$S ListRes}
  242.  
  243. PROCEDURE TDynamicArray.GetElementsAt(index: ArrayIndex;
  244.                                       ElementPtr: UNIV Ptr;
  245.                                       count: ArrayIndex);
  246.  
  247.     BEGIN
  248.     IF qRangeCheck & ((index < 1) | (index > fSize)) THEN
  249.         BEGIN
  250.         Writeln('fSize = ', fSize: 1, '  index = ', index: 1);
  251.         ProgramBreak('Range Check in TDynamicArray.ReplaceElementAt');
  252.         END;
  253.     { The count computation for this blockmove makes sure that if the element size is not
  254.     a power of 2 that we don't copy more bytes back to the caller than required for a
  255.     given number of elements… sending the caller into orbit and heading straight for the Excedrin.
  256.  
  257.     !!! NOTE MULTIPLE (> 1) element moves for non-power of 2 element sizes are not yet supported! }
  258.  
  259.     IF count > kEmptyIndex THEN
  260.         BlockMove(ComputeAddress(index), ElementPtr, BSL(count - 1, fElementSizeShift) +
  261.                   fElementSize);
  262.     END;
  263.  
  264. {--------------------------------------------------------------------------------------------------}
  265. {$S ListRes}
  266. {$Push} {$IFC qTrace} {$D+} {$ENDC}{ Called _FREQUENTLY_ }
  267.  
  268. FUNCTION TDynamicArray.ComputeAddress(index: ArrayIndex): Ptr;
  269.     VAR
  270.         p: Ptr;
  271.  
  272.     BEGIN
  273.     {$IFC FALSE}
  274.     { This is the ideal situation.  Unfortunately it is too slow for something as
  275.     widely used as Dynamic Arrays, so we break the encapsulation below.  If your subclass
  276.     wishes to manage storage gotten from somewhere other than the object's handle then
  277.     you must override GetDynamicPtr to return a pointer to the storage to be managed and
  278.     ALSO override ComputeAddress to use this commented out code. }
  279.  
  280.     p := GetDynamicPtr;
  281.     IF qDebug & (p = NIL) THEN
  282.         DebugStr('ComputeAddress called with an empty list (no dynamic area allocated)');
  283.  
  284.     { p + ((index - 1) * fElementSize) }
  285.     ComputeAddress := Ptr(ord(p) + BSL((index - 1), fElementSizeShift));
  286.     {$EndC}
  287.  
  288.     { Break encapsulation and just return an index off of SELF which we know to be a handle
  289.     MacApp 2.0 }
  290.     ComputeAddress := Ptr(striplong(Handle(SELF)^) + fClassSize + BSL((index - 1), fElementSizeShift));
  291.     END;
  292. {$Pop}
  293.  
  294. {--------------------------------------------------------------------------------------------------}
  295. {$S ListRes}
  296.  
  297. PROCEDURE TDynamicArray.Free;
  298.  
  299.     BEGIN
  300.     IF fTailNodePtr <> NIL THEN                         { She can't do it cap'n. The dilithium
  301.                                                          crystals are burrrrnt out! If they're
  302.                                                          allowed to rest a while they might recover
  303.                                                          enough for another try. }
  304.         BEGIN
  305.         fFreeRequested := TRUE;
  306.         { Release our dynamic portion now, but wait to be freed }
  307.         IF fSize > kEmptyIndex THEN
  308.             DeleteElementsAt(1, fSize);
  309.         END
  310.     ELSE
  311.         INHERITED Free;
  312.  
  313.     END;
  314.  
  315. {--------------------------------------------------------------------------------------------------}
  316. {$S ListRes}
  317.  
  318. FUNCTION TDynamicArray.GetSize: ArrayIndex;
  319.  
  320.     BEGIN
  321.     GetSize := fSize;
  322.     END;
  323.  
  324. {--------------------------------------------------------------------------------------------------}
  325. {$S ListRes}
  326.  
  327. PROCEDURE TDynamicArray.InsertElementsBefore(index: ArrayIndex;
  328.                                              ElementPtr: UNIV Ptr;
  329.                                              count: ArrayIndex);
  330.  
  331.     VAR
  332.         indexPtr, nextIndexPtr, lastElementPtr: Ptr;
  333.         countBytes:         ArrayIndex;
  334.  
  335.     PROCEDURE DoToNode(thisNode: UNIV IterationNodePtr);
  336.     { bends the index to account for the inserted elements }
  337.  
  338.         BEGIN
  339.         WITH thisNode^ DO
  340.             BEGIN
  341.             IF iterForward THEN
  342.                 BEGIN
  343.                 { If the inserted element was NOT in the
  344.                 range yet to be iterated then bend the
  345.                 iterIndex to account for it. }
  346.                 IF index <= iterLowBound THEN
  347.                     iterLowBound := iterLowBound + count;
  348.  
  349.                 IF index <= iterIndex THEN
  350.                     iterIndex := iterIndex + count;
  351.  
  352.                 IF index <= iterHighBound THEN
  353.                     iterHighBound := iterHighBound + count;
  354.                 END
  355.             ELSE                                        { Iterating backward }
  356.                 BEGIN
  357.                 { If the inserted element was IN the range
  358.                 yet to be iterated then bend the iterIndex
  359.                 to account for it. }
  360.                 IF index <= iterLowBound THEN
  361.                     iterLowBound := iterLowBound + count;
  362.  
  363.                 IF index < iterIndex THEN
  364.                     iterIndex := iterIndex + count;
  365.  
  366.                 IF index <= iterHighBound THEN
  367.                     iterHighBound := iterHighBound + count;
  368.                 END;
  369.             END;
  370.         END;
  371.  
  372.     BEGIN
  373.     IF qRangeCheck & ((index < 1) | (index > fSize + 1)) THEN
  374.         BEGIN
  375.         Writeln('fSize = ', fSize: 1, '  index = ', index: 1);
  376.         ProgramBreak('Range Check in TDynamicArray.InsertBefore');
  377.         END;
  378.  
  379.     SetArraySize(fSize + count);                        { make sure there's room if needed }
  380.  
  381.     indexPtr := ComputeAddress(index);
  382.     nextIndexPtr := ComputeAddress(index + count);
  383.     lastElementPtr := ComputeAddress(fSize + 1);
  384.     countBytes := BSL(count, fElementSizeShift);
  385.  
  386.     IF index <= fSize THEN                                { clear out a hole? }
  387.         BlockMove(indexPtr, nextIndexPtr, ord(lastElementPtr) - ord(indexPtr));
  388.  
  389.     { !!! we still don't account for multiple element moves with non power of 2 element sizes.
  390.     Would it be best to create a MoveElements method and put the smarts in it? }
  391.  
  392.     IF (countBytes = sizeof(longint)) & NOT odd(ord(ElementPtr)) & NOT odd(ord(indexPtr)) THEN
  393.         LongintPtr(indexPtr)^ := LongintPtr(ElementPtr)^ { shortcut for longs }
  394.     ELSE
  395.         BlockMove(ElementPtr, indexPtr, countBytes);    { longcut for shorts (and other sizes) }
  396.  
  397.     fSize := fSize + count;
  398.  
  399.     IF fTailNodePtr <> NIL THEN
  400.         EachNodeDo(DoToNode);
  401.     END;
  402.  
  403. {--------------------------------------------------------------------------------------------------}
  404. {$S ListRes}
  405.  
  406. FUNCTION TDynamicArray.IsEmpty: Boolean;
  407.  
  408.     BEGIN
  409.     IsEmpty := fSize = kEmptyIndex;
  410.     END;
  411.  
  412. {--------------------------------------------------------------------------------------------------}
  413. {$S ListRes}
  414.  
  415. FUNCTION TDynamicArray.EachElementDoTil(FUNCTION TestElement(elementIndex: ArrayIndex): Boolean;
  416.                                         IterateForward: Boolean): ArrayIndex;
  417. { DON'T use EXIT to get out of this routine from your TestElement function or you will be really
  418. sad!  (our debugger will check for you) That's why you can return TRUE to stop enumerating.
  419. Signaling Failure is OK too. }
  420.  
  421.     VAR
  422.         fi:                 FailInfo;
  423.         myIterationNode:    IterationNode;
  424.  
  425.     PROCEDURE HdlEachElementDoTil(error: OSErr;
  426.                             message: longint);
  427.  
  428.         BEGIN
  429.         RemoveNode(@myIterationNode);
  430.         END;
  431.  
  432.     BEGIN
  433.     EachElementDoTil := kEmptyIndex;
  434.     IF fSize > kEmptyIndex THEN                         { Can't even special case one element lists,
  435.                                                          wouldn't be sure of index to return if the
  436.                                                          TestElement call inserted or deleted. }
  437.         BEGIN
  438.         WITH myIterationNode DO                         { Make sure that the iterIndex counter and
  439.                                                          Iteration direction flag from
  440.                                                          myIterationNode are used so that the
  441.                                                          iterIndex counter can be "bent" if anyone
  442.                                                          else deletes or inserts elements while the
  443.                                                          iteration is in progress. Pretty slick,
  444.                                                          eh? }
  445.             BEGIN
  446.             AppendNode(@myIterationNode);                { link me in to the list of iterations in
  447.                                                          progress }
  448.             CatchFailures(fi, HdlEachElementDoTil);
  449.  
  450.             iterForward := IterateForward;
  451.  
  452.             { Note that outer bound of the loop is the value of fSize at the
  453.             time the loop is begun. Changing fSize in the loop has no
  454.             effect on the number of times the loop is executed. }
  455.  
  456.             iterLowBound := 1;
  457.             iterHighBound := fSize;
  458.  
  459.             IF IterateForward THEN
  460.                 BEGIN
  461.                 iterIndex := iterLowBound;
  462.                 REPEAT
  463.                     IF TestElement(iterIndex) THEN
  464.                         LEAVE;
  465.                     iterIndex := iterIndex + 1;
  466.                 UNTIL iterIndex > iterHighBound;
  467.                 END
  468.             ELSE
  469.                 BEGIN
  470.                 iterIndex := iterHighBound;
  471.                 REPEAT
  472.                     IF TestElement(iterIndex) THEN
  473.                         LEAVE;
  474.                     iterIndex := iterIndex - 1;
  475.                 UNTIL iterIndex < iterLowBound;
  476.                 END;
  477.  
  478.             { keep index in range }
  479.             IF (iterIndex < kEmptyIndex) | (iterIndex > fSize) THEN
  480.                 EachElementDoTil := kEmptyIndex
  481.             ELSE
  482.                 EachElementDoTil := iterIndex;
  483.  
  484.             Success(fi);
  485.             RemoveNode(@myIterationNode);
  486.  
  487.             { Check if there is a pending free request that couldn't be honored because we were
  488.             iterating and if so… be free! }
  489.             IF fFreeRequested & (fTailNodePtr = NIL) THEN
  490.                 Free;
  491.  
  492.             END;
  493.         END;
  494.     END;
  495.  
  496. {--------------------------------------------------------------------------------------------------}
  497. {$S ListRes}
  498.  
  499. PROCEDURE TDynamicArray.Merge(aDynamicArray: TDynamicArray);
  500.  
  501.     BEGIN
  502.     IF qDebug & ((fElementSize <> aDynamicArray.fElementSize) | (fElementSizeShift <>
  503.        aDynamicArray.fElementSizeShift)) THEN
  504.         ProgramBreak(
  505. 'In TDynamicArray.Merge: fElementSize or fElementSizeShift don''t match with the TDynamicArray to merge!'
  506.                      );
  507.  
  508.     IF aDynamicArray.GetSize <> kEmptyIndex THEN
  509.         InsertElementsBefore(GetSize + 1, aDynamicArray.ComputeAddress(1), aDynamicArray.GetSize);
  510.     END;
  511.  
  512. {--------------------------------------------------------------------------------------------------}
  513. {$S ListRes}
  514.  
  515. PROCEDURE TDynamicArray.ReplaceElementsAt(index: ArrayIndex;
  516.                                           ElementPtr: UNIV Ptr;
  517.                                           count: ArrayIndex);
  518.  
  519.     BEGIN
  520.     IF qRangeCheck & ((index < 1) | (index > fSize)) THEN
  521.         BEGIN
  522.         Writeln('fSize = ', fSize: 1, '  index = ', index: 1);
  523.         ProgramBreak('Range Check in TDynamicArray.ReplaceElementAt');
  524.         END;
  525.  
  526.     BlockMove(ElementPtr, ComputeAddress(index), BSL(count, fElementSizeShift));
  527.     END;
  528.  
  529. {--------------------------------------------------------------------------------------------------}
  530. {$S ListRes}
  531.  
  532. PROCEDURE TDynamicArray.SetArraySize(theSize: ArrayIndex);
  533.  
  534.     VAR
  535.         newAllocatedSize:    ArrayIndex;
  536.  
  537.     BEGIN
  538.     IF (theSize > fAllocatedSize) | (fAllocatedSize - theSize >= fAllocationIncrement) THEN
  539.         BEGIN
  540.  
  541.         IF qDebug & (fAllocationIncrement < 0) THEN
  542.             ProgramBreak('fAllocationIncrement < 0 !  You have serious problems.');
  543.  
  544.         { Set the # of allocated elements to the nearest multiple of fAllocationIncrement.
  545.         Wait until after the SetDynamicSize to set fAllocatedSize in case SetDynamicSize
  546.         signals failure. }
  547.         IF fAllocationIncrement <> 0 THEN
  548.             newAllocatedSize := (theSize + fAllocationIncrement) - (theSize + fAllocationIncrement)
  549.             MOD fAllocationIncrement
  550.         ELSE
  551.             newAllocatedSize := theSize;
  552.  
  553.         IF newAllocatedSize <> fAllocatedSize THEN
  554.             SetDynamicSize(BSL(newAllocatedSize, fElementSizeShift));
  555.  
  556.         fAllocatedSize := newAllocatedSize;
  557.         END;
  558.     END;
  559.  
  560. {--------------------------------------------------------------------------------------------------}
  561. {$S MAFields}
  562.  
  563. PROCEDURE TDynamicArray.Fields(PROCEDURE DoToField(fieldName: Str255;
  564.                                                    fieldAddr: Ptr;
  565.                                                    fieldType: integer)); OVERRIDE;
  566.  
  567.     BEGIN
  568.     DoToField('TDynamicArray', NIL, bClass);
  569.     DoToField('fSize', @fSize, bLongInt);
  570.     DoToField('fElementSize', @fElementSize, bInteger);
  571.     DoToField('fElementSizeShift', @fElementSizeShift, bInteger);
  572.     DoToField('fAllocationIncrement', @fAllocationIncrement, bLongInt);
  573.     DoToField('fAllocatedSize', @fAllocatedSize, bLongInt);
  574.     DoToField('fFreeRequested', @fFreeRequested, bBoolean);
  575.     DoToField('fClassSize', @fClassSize, bLongInt);
  576.  
  577.     INHERITED Fields(DoToField);
  578.     END;
  579.  
  580. {--------------------------------------------------------------------------------------------------}
  581. {$S MAFields}
  582.  
  583. PROCEDURE TDynamicArray.DynamicFields(PROCEDURE DoToField(fieldName: Str255;
  584.                                                           fieldAddr: Ptr;
  585.                                                           fieldType: integer)); OVERRIDE;
  586.  
  587.     FUNCTION DoToElement(theIndex: ArrayIndex): Boolean;
  588.  
  589.         VAR
  590.             aString:            Str255;
  591.  
  592.         BEGIN
  593.         DoToElement := FALSE;
  594.         NumToString(theIndex, aString);
  595.         aString := CONCAT('.ComputeAddress[', aString, ']');
  596.         DoToField(aString, ComputeAddress(theIndex), bPointer);
  597.         END;
  598.  
  599.     BEGIN
  600.     DoToField('Dynamic Fields', NIL, bTitle);
  601.  
  602.     IF EachElementDoTil(DoToElement, kIterateForward) <> kEmptyIndex THEN;
  603.     END;
  604.  
  605. {--------------------------------------------------------------------------------------------------}
  606. {$S ListRes}
  607.  
  608. FUNCTION NewList: TList;
  609.  
  610.     VAR
  611.         list:                TList;
  612.  
  613.     BEGIN
  614.     New(list);
  615.     FailNil(list);
  616.     list.IList;
  617.     NewList := list;
  618.     END;
  619.  
  620. {--------------------------------------------------------------------------------------------------}
  621. {$S ListRes}
  622.  
  623. FUNCTION NewSortedList: TSortedList;
  624.  
  625.     VAR
  626.         list:                TSortedList;
  627.  
  628.     BEGIN
  629.     New(list);
  630.     FailNil(list);
  631.     list.IList;
  632.     NewSortedList := list;
  633.     END;
  634.  
  635. {--------------------------------------------------------------------------------------------------}
  636. {$S ListRes}
  637.  
  638. FUNCTION NewAllocatedList(iSize: ArrayIndex): TList;
  639.  
  640.     VAR
  641.         list:                TList;
  642.  
  643.     BEGIN
  644.     list := NewList;
  645.     list.SetArraySize(iSize);
  646.     NewAllocatedList := list;
  647.     END;
  648.  
  649. {--------------------------------------------------------------------------------------------------}
  650. {$S ListRes}
  651.  
  652. FUNCTION FreeListIfObject(list: TList): TList;
  653.  
  654.     BEGIN
  655.     FreeListIfObject := NIL;    { for convenience of caller }
  656.  
  657.     IF list <> NIL THEN
  658.         BEGIN
  659.         if qDebug THEN
  660.             FailNonObject(list);
  661.  
  662.         list.FreeList;
  663.         END;
  664.     END;
  665.  
  666. {--------------------------------------------------------------------------------------------------}
  667. {$S ListRes}
  668.  
  669. PROCEDURE TList.IList;
  670.  
  671.     BEGIN
  672.     IDynamicArray(kEmptyIndex, sizeof(Handle));         { Can't do sizeof(TObject) because sizeof
  673.                                                          returns the record size for objects }
  674.     fObjClassID := kNilClass;
  675.     END;
  676.  
  677. {--------------------------------------------------------------------------------------------------}
  678. {$S ListRes}
  679.  
  680. FUNCTION TList.At(index: ArrayIndex): TObject;
  681.  
  682.     BEGIN
  683.     IF qRangeCheck & ((index <= kEmptyIndex) | (index > fSize)) THEN
  684.         BEGIN
  685.         Writeln('fSize = ', fSize: 1, '  index = ', index: 1);
  686.         ProgramBreak('Range Check in TList.At');
  687.         END;
  688.  
  689.     At := TObjectPtr(ComputeAddress(index))^;
  690.     END;
  691.  
  692. {--------------------------------------------------------------------------------------------------}
  693. {$S ListRes}
  694.  
  695. PROCEDURE TList.AtDelete(index: ArrayIndex);
  696.  
  697.     BEGIN
  698.     IF qRangeCheck & ((index <= kEmptyIndex) | (index > fSize)) THEN
  699.         BEGIN
  700.         Writeln('fSize = ', fSize: 1, '  index = ', index: 1);
  701.         ProgramBreak('Range Check in TList.AtDelete');
  702.         END;
  703.  
  704.     DeleteElementsAt(index, 1);
  705.     END;
  706.  
  707. {--------------------------------------------------------------------------------------------------}
  708. {$S ListRes}
  709.  
  710. PROCEDURE TList.AtPut(index: ArrayIndex;
  711.                       newItem: TObject);
  712.  
  713.     BEGIN
  714.     IF qRangeCheck & ((index <= kEmptyIndex) | (index > fSize)) THEN
  715.         BEGIN
  716.         Writeln('fSize = ', fSize: 1, '  index = ', index: 1);
  717.         ProgramBreak('Range Check in TList.AtPut');
  718.         Failure(minErr, 0);                             { Can't continue, bad index trashes memory
  719.                                                          ??? Would it be nice to have a mechanism
  720.                                                          where the developer could supply a new
  721.                                                          index here? }
  722.         END;
  723.  
  724.     IF qDebug THEN
  725.         BEGIN
  726.         FailNonObject(newItem);
  727.         IF (fObjClassID <> kNilClass) THEN
  728.             IF NOT IsMemberClassID(newItem, fObjClassID) THEN
  729.                 BEGIN
  730.                 WrLblPtr('newItem', newItem);
  731.                 Writeln;
  732.                 ProgramBreak('In TList.AtPut: inserting invalid object type');
  733.                 END;
  734.         END;
  735.  
  736.     TObjectPtr(ComputeAddress(index))^ := newItem;
  737.     END;
  738.  
  739. {--------------------------------------------------------------------------------------------------}
  740. {$S ListRes}
  741.  
  742. PROCEDURE TList.Delete(item: TObject);
  743.  
  744.     VAR
  745.         index:                ArrayIndex;
  746.  
  747.     BEGIN
  748.     IF qDebug THEN
  749.         FailNonObject(item);
  750.     index := GetSameItemNo(item);
  751.     IF index <> kEmptyIndex THEN
  752.         AtDelete(index);
  753.     END;
  754.  
  755. {--------------------------------------------------------------------------------------------------}
  756. {$S ListRes}
  757.  
  758. PROCEDURE TList.DeleteAll;
  759.  
  760.     BEGIN
  761.     IF fSize > kEmptyIndex THEN
  762.         DeleteElementsAt(1, fSize);
  763.     END;
  764.  
  765. {--------------------------------------------------------------------------------------------------}
  766. {$S ListRes}
  767.  
  768. PROCEDURE TList.Each(PROCEDURE DoToItem(item: TObject));
  769.  
  770.     VAR
  771.         index:                ArrayIndex;
  772.  
  773.     {$Push} {$IFC qTrace} {$D+} {$ENDC}
  774.     FUNCTION TestItem(item: TObject): Boolean;
  775.  
  776.         BEGIN
  777.         DoToItem(item);
  778.         TestItem := FALSE;
  779.         END;
  780.     {$Pop}
  781.  
  782.     BEGIN
  783.     { just use IterateTil with a function that never returns true }
  784.     IF IterateTil(TestItem, kIterateForward, index) <> NIL THEN;
  785.     END;
  786.  
  787. {--------------------------------------------------------------------------------------------------}
  788. {$S ListRes}
  789.  
  790. FUNCTION TList.First: TObject;
  791.  
  792.     BEGIN
  793.     IF fSize <= kEmptyIndex THEN
  794.         First := NIL
  795.     ELSE
  796.         First := At(1);
  797.     END;
  798.  
  799. {--------------------------------------------------------------------------------------------------}
  800. {$S ListRes}
  801.  
  802. FUNCTION TList.FirstThat(FUNCTION TestItem(item: TObject): Boolean): TObject;
  803.  
  804.     VAR
  805.         index:                ArrayIndex;
  806.  
  807.     BEGIN
  808.     FirstThat := IterateTil(TestItem, kIterateForward, index);
  809.     END;
  810.  
  811. {--------------------------------------------------------------------------------------------------}
  812. {$S ListRes}
  813.  
  814. PROCEDURE TList.FreeAll;
  815.  
  816.     BEGIN
  817.     Each(FreeIfObject);
  818.     DeleteAll;
  819.     END;
  820.  
  821. {--------------------------------------------------------------------------------------------------}
  822. {$S ListRes}
  823.  
  824. PROCEDURE TList.FreeList;
  825.  
  826.     BEGIN
  827.     Each(FreeIfObject);
  828.     Free;
  829.     END;
  830.  
  831. {--------------------------------------------------------------------------------------------------}
  832. {$S ListRes}
  833.  
  834. FUNCTION TList.GetSameItemNo(item: TObject): ArrayIndex;
  835.  
  836.     VAR
  837.         index:                ArrayIndex;
  838.  
  839.     {$Push} {$IFC qTrace} {$D+} {$ENDC}
  840.     FUNCTION TestItem(listItem: TObject): Boolean;
  841.  
  842.         BEGIN
  843.         IF listItem = item THEN
  844.             TestItem := TRUE
  845.         ELSE
  846.             TestItem := FALSE;
  847.         END;
  848.         {$Pop}
  849.  
  850.     BEGIN
  851.     IF qDebug THEN
  852.         FailNonObject(item);
  853.     { the equality test should not change the list's size or the resulting index can be invalid }
  854.     IF IterateTil(TestItem, kIterateForward, index) <> NIL THEN;
  855.  
  856.     GetSameItemNo := index;
  857.     END;
  858.  
  859. {--------------------------------------------------------------------------------------------------}
  860. {$S ListRes}
  861.  
  862. FUNCTION TList.GetEqualItemNo(item: TObject): ArrayIndex;
  863.  
  864.     BEGIN
  865.     IF qDebug THEN
  866.         FailNonObject(item);
  867.     GetEqualItemNo := GetSameItemNo(item);
  868.     END;
  869.  
  870. {--------------------------------------------------------------------------------------------------}
  871. {$S ListRes}
  872.  
  873. PROCEDURE TList.Insert(item: TObject);
  874.  
  875.     BEGIN
  876.     { Depend on InsertBefore for sanity checking }
  877.     InsertLast(item);
  878.     END;
  879.  
  880. {--------------------------------------------------------------------------------------------------}
  881. {$S ListRes}
  882.  
  883. PROCEDURE TList.InsertBefore(index: ArrayIndex;
  884.                              item: TObject);
  885.  
  886.     BEGIN
  887.     IF qRangeCheck & ((index <= kEmptyIndex) | (index > fSize + 1)) THEN
  888.         BEGIN
  889.         Writeln('fSize = ', fSize: 1, '  index = ', index: 1);
  890.         ProgramBreak('Range Check in TList.InsertBefore');
  891.         END;
  892.  
  893.     IF qDebug THEN
  894.         BEGIN
  895.         FailNonObject(item);
  896.         IF (fObjClassID <> kNilClass) THEN
  897.             IF NOT IsMemberClassID(item, fObjClassID) THEN
  898.                 BEGIN
  899.                 WrLblPtr('item', item);
  900.                 Writeln;
  901.                 ProgramBreak('In TList.InsertBefore: inserting invalid object type');
  902.                 END;
  903.         END;
  904.  
  905.     InsertElementsBefore(index, @item, 1);
  906.     END;
  907.  
  908. {--------------------------------------------------------------------------------------------------}
  909. {$S ListRes}
  910.  
  911. PROCEDURE TList.InsertFirst(item: TObject);
  912.  
  913.     BEGIN
  914.     { Depend on InsertBefore for sanity checking }
  915.     InsertBefore(1, item);
  916.     END;
  917.  
  918. {--------------------------------------------------------------------------------------------------}
  919.  
  920. PROCEDURE TList.InsertLast(item: TObject);
  921.  
  922.     BEGIN
  923.     { Depend on InsertBefore for sanity checking }
  924.     InsertBefore(fSize + 1, item);
  925.     END;
  926.  
  927. {--------------------------------------------------------------------------------------------------}
  928. {$S ListRes}
  929.  
  930. FUNCTION TList.IterateTil(FUNCTION TestItem(item: TObject): Boolean;
  931.                           IterateForward: Boolean;
  932.                           VAR itsIndex: ArrayIndex): TObject;
  933.  
  934.     VAR
  935.         theObjectToReturn:    TObject;
  936.  
  937.     {$Push} {$IFC qTrace} {$D+} {$ENDC}
  938.     FUNCTION DoToElement(theIndex: ArrayIndex): Boolean;
  939.  
  940.         VAR
  941.             testResult:         Boolean;
  942.             testedObject:        TObject;
  943.  
  944.         BEGIN
  945.         testedObject := At(theIndex);                    { we save the object in case the test
  946.                                                          process removes it from the list }
  947.         testResult := TestItem(testedObject);
  948.         DoToElement := testResult;
  949.  
  950.         { If the test returned true then we need to return the object.  We have to do this
  951.         with a temp because the object may have been removed from the list by TestItem }
  952.         IF testResult THEN
  953.             IterateTil := testedObject;
  954.         END;
  955.         {$Pop}
  956.  
  957.     BEGIN
  958.     IterateTil := NIL;                                    { if TestItem returns true the
  959.                                                          IterateTil will be set to the
  960.                                                          object that tested true }
  961.     itsIndex := EachElementDoTil(DoToElement, IterateForward);
  962.     END;
  963.  
  964. {--------------------------------------------------------------------------------------------------}
  965. {$S ListRes}
  966.  
  967. FUNCTION TList.Last: TObject;
  968.  
  969.     BEGIN
  970.     IF fSize <= kEmptyIndex THEN
  971.         Last := NIL
  972.     ELSE
  973.         Last := At(fSize);
  974.     END;
  975.  
  976. {--------------------------------------------------------------------------------------------------}
  977. {$S ListRes}
  978.  
  979. FUNCTION TList.LastThat(FUNCTION TestItem(item: TObject): Boolean): TObject;
  980.  
  981.     VAR
  982.         index:                ArrayIndex;
  983.  
  984.     BEGIN
  985.     LastThat := IterateTil(TestItem, kIterateBackward, index);
  986.     END;
  987.  
  988. {--------------------------------------------------------------------------------------------------}
  989. {$S ListDebug}
  990.  
  991. PROCEDURE TList.SetEltType(toClass: MAName);
  992.  
  993.     VAR
  994.         s:                    MAName;
  995.  
  996.     BEGIN
  997.     { Forgive the caller for calling this twice only if the same type was specified both times }
  998.     IF qDebug & (fObjClassID <> kNilClass) THEN
  999.         BEGIN
  1000.         GetClassNameFromID(fObjClassID, s);
  1001.         IF toClass <> s THEN
  1002.             BEGIN
  1003.             Writeln('The list already contains ', s, '; trying to change to ', toClass);
  1004.             ProgramBreak('In TList.SetEltType');
  1005.             END;
  1006.         END;
  1007.     { Assign the field }
  1008.     fObjClassID := GetClassIDFromName(toClass)            { srf 88.9.7 }
  1009.     END;
  1010.  
  1011. {--------------------------------------------------------------------------------------------------}
  1012. {$S ListDebug}
  1013.  
  1014. PROCEDURE TList.SetEltTypeID(toClassID: ObjClassID);
  1015.  
  1016.     VAR
  1017.         s, newClassName:    MAName;
  1018.  
  1019.     BEGIN
  1020.     { Forgive the caller for calling this twice only if the same type was specified both times }
  1021.     IF qDebug & (fObjClassID <> kNilClass) THEN
  1022.         BEGIN
  1023.         IF fObjClassID <> toClassID THEN
  1024.             BEGIN
  1025.             GetClassNameFromID(fObjClassID, s);
  1026.             GetClassNameFromID(toClassID, newClassName);
  1027.             Writeln('The list already contains ', s, '; trying to change to ', newClassName);
  1028.             ProgramBreak('In TList.SetEltTypeID');
  1029.             END;
  1030.         END;
  1031.     { Assign the field }
  1032.     fObjClassID := toClassID;
  1033.     END;
  1034.  
  1035. {--------------------------------------------------------------------------------------------------}
  1036. {$S ListRes}
  1037.  
  1038. PROCEDURE TList.SortBy(FUNCTION CompareItems(item1, item2: TObject): CompareResult);
  1039. {!!! Sort would be nice to have at the TDynamicArray level too…  Parameterized types where are you?
  1040. NOTE: This doesn't work with a CompareItems Function that inserts or deletes elements. }
  1041.  
  1042.     VAR
  1043.         i, j, h:            ArrayIndex;
  1044.         v, item:            TObject;
  1045.  
  1046.     BEGIN
  1047.     { Do a nice shell sort.  …For _really_ big lists this isn't fast enough }
  1048.     {Initialize}
  1049.     h := 1;
  1050.     REPEAT
  1051.         h := 3 * h + 1
  1052.     UNTIL h > fSize;
  1053.  
  1054.     {Sort}
  1055.     REPEAT
  1056.         h := h DIV 3;
  1057.         FOR i := h + 1 TO fSize DO
  1058.             BEGIN
  1059.             v := At(i);
  1060.             j := i;
  1061.             item := At(j - h);
  1062.             WHILE CompareItems(item, v) >= kItem1GreaterThanItem2 DO
  1063.                 BEGIN
  1064.                 AtPut(j, item);
  1065.                 j := j - h;
  1066.                 IF j <= h THEN
  1067.                     LEAVE;
  1068.                 item := At(j - h);
  1069.                 END;
  1070.             AtPut(j, v);
  1071.             END;
  1072.     UNTIL h = 1;
  1073.     END;
  1074.  
  1075. {--------------------------------------------------------------------------------------------------}
  1076. {$S MAFields}
  1077.  
  1078. PROCEDURE TList.Fields(PROCEDURE DoToField(fieldName: Str255;
  1079.                                            fieldAddr: Ptr;
  1080.                                            fieldType: integer)); OVERRIDE;
  1081.  
  1082.     VAR
  1083.         aString:            MAName;
  1084.  
  1085.     BEGIN
  1086.     DoToField('TList', NIL, bClass);
  1087.  
  1088.     GetClassNameFromID(fObjClassID, aString);
  1089.     DoToField('fObjClassID', @aString, bString);
  1090.  
  1091.     INHERITED Fields(DoToField);
  1092.     END;
  1093.  
  1094. {--------------------------------------------------------------------------------------------------}
  1095. {$S MAFields}
  1096.  
  1097. PROCEDURE TList.DynamicFields(PROCEDURE DoToField(fieldName: Str255;
  1098.                                                   fieldAddr: Ptr;
  1099.                                                   fieldType: integer)); OVERRIDE;
  1100.  
  1101.     VAR
  1102.         i:                    ArrayIndex;
  1103.         aString:            Str255;
  1104.  
  1105.     PROCEDURE ShowEntry(obj: TObject);
  1106.  
  1107.         BEGIN
  1108.         i := i + 1;
  1109.         NumToString(i, aString);
  1110.         aString := CONCAT('.At[', aString, ']');
  1111.         DoToField(aString, @obj, bObject);
  1112.         END;
  1113.  
  1114.     BEGIN
  1115.     DoToField('Dynamic Fields', NIL, bTitle);
  1116.  
  1117.     i := kEmptyIndex;
  1118.     Each(ShowEntry);
  1119.     END;
  1120.  
  1121. {--------------------------------------------------------------------------------------------------}
  1122. {$S MAInspector}
  1123.  
  1124. PROCEDURE TList.GetInspectorName(VAR inspectorName: Str255); OVERRIDE;
  1125.  
  1126.     VAR
  1127.         aStringPtr:         PMAName;
  1128.  
  1129.     BEGIN
  1130.     IF fObjClassID <> kNilClass THEN
  1131.         BEGIN
  1132.         aStringPtr := PMAName(@inspectorName);
  1133.         GetClassNameFromID(fObjClassID, aStringPtr^);
  1134.         inspectorName := CONCAT('Of ', inspectorName);
  1135.         inspectorName := CONCAT(inspectorName, ' Size: ');
  1136.         inspectorName := CONCATNUMBER(inspectorName, GetSize);
  1137.         END;
  1138.     END;
  1139.  
  1140. {--------------------------------------------------------------------------------------------------}
  1141. {$S ListRes}
  1142.  
  1143. PROCEDURE TList.Push(item: TObject);
  1144.  
  1145.     BEGIN
  1146.     { Depend on InsertBefore for sanity checking }
  1147.     InsertLast(item);
  1148.     END;
  1149.  
  1150. {--------------------------------------------------------------------------------------------------}
  1151. {$S ListRes}
  1152.  
  1153. FUNCTION TList.Pop: TObject;
  1154.  
  1155.     BEGIN
  1156.     IF fSize = kEmptyIndex THEN
  1157.         Pop := NIL
  1158.     ELSE
  1159.         BEGIN
  1160.         Pop := At(fSize);
  1161.         AtDelete(fSize);
  1162.         END;
  1163.     END;
  1164.  
  1165. {--------------------------------------------------------------------------------------------------}
  1166. {$S ListRes}
  1167.  
  1168. FUNCTION TSortedList.Compare(item1, item2: TObject): CompareResult;
  1169.  
  1170.     BEGIN
  1171.     IF ord(item1) > ord(item2) THEN
  1172.         Compare := kItem1GreaterThanItem2
  1173.     ELSE IF ord(item1) < ord(item2) THEN
  1174.         Compare := kItem1LessThanItem2
  1175.     ELSE
  1176.         Compare := kItem1EqualItem2;
  1177.     END;
  1178.  
  1179. {--------------------------------------------------------------------------------------------------}
  1180. {$S ListRes}
  1181.  
  1182. PROCEDURE TSortedList.ISortedList;
  1183.  
  1184.     BEGIN
  1185.     IList;                                                { This method is not strictly required right
  1186.                                                          now but we want to require calling it }
  1187.     END;
  1188.  
  1189. {--------------------------------------------------------------------------------------------------}
  1190. {$S ListRes}
  1191.  
  1192. FUNCTION TSortedList.DoSearch(FUNCTION TestItem(anItem: TObject): CompareResult;
  1193.                               VAR index: ArrayIndex): TObject;
  1194.  
  1195. { DON'T use EXIT to get out of this routine from your TestItem function or you will be really
  1196. sad!  (our debugger will check for you) That's why you can return TRUE to stop enumerating.
  1197. Signaling Failure is OK too. }
  1198.  
  1199.     VAR
  1200.         fi:                 FailInfo;
  1201.         myIterationNode:    IterationNode;
  1202.         aCompareResult:     CompareResult;
  1203.         obj:                TObject;
  1204.  
  1205.     PROCEDURE HdlDoSearch(error: OSErr;
  1206.                             message: longint);
  1207.  
  1208.         BEGIN
  1209.         RemoveNode(@myIterationNode);
  1210.         END;
  1211.  
  1212.     BEGIN
  1213.     DoSearch := NIL;
  1214.  
  1215.     IF fSize = kEmptyIndex THEN
  1216.         index := 1
  1217.     ELSE
  1218.         WITH myIterationNode DO                         { Make sure that the iterIndex counter and
  1219.                                                          Iteration direction flag from
  1220.                                                          myIterationNode are used so that the
  1221.                                                          iterIndex counter can be "bent" if anyone
  1222.                                                          else deletes or inserts elements while the
  1223.                                                          iteration is in progress. Pretty slick,
  1224.                                                          eh? }
  1225.             BEGIN
  1226.             AppendNode(@myIterationNode);                { link me in to the list of iterations in
  1227.                                                          progress }
  1228.             CatchFailures(fi, HdlDoSearch);
  1229.  
  1230.             iterForward := kIterateForward;
  1231.             iterLowBound := 1;
  1232.             iterHighBound := fSize;
  1233.  
  1234.             REPEAT
  1235.                 iterIndex := BSR(iterLowBound + iterHighBound, 1); { (iterLowBound + iterHighBound)
  1236.                                                                     DIV 2 }
  1237.  
  1238.                 IF qDebug THEN
  1239.                     FailNonObject(At(iterIndex));
  1240.  
  1241.                 obj := At(iterIndex);        { in case the index is deleted. }
  1242.                 aCompareResult := TestItem(obj);
  1243.  
  1244.                 IF aCompareResult <= kItemGreaterThanCriteria THEN
  1245.                     iterHighBound := iterIndex - 1
  1246.                 ELSE
  1247.                     iterLowBound := iterIndex + 1;
  1248.  
  1249.             UNTIL (aCompareResult = kItemEqualCriteria) | (iterLowBound > iterHighBound);
  1250.  
  1251.             IF aCompareResult = kItemEqualCriteria THEN
  1252.                 DoSearch := obj
  1253.             ELSE IF aCompareResult >= kItemLessThanCriteria THEN
  1254.                 iterIndex := iterIndex + 1;
  1255.  
  1256.             { keep index in range }
  1257.             IF (iterIndex < 1) | (iterIndex > fSize + 1) THEN
  1258.                 index := kEmptyIndex
  1259.             ELSE
  1260.                 index := iterIndex;
  1261.  
  1262.             Success(fi);
  1263.             RemoveNode(@myIterationNode);
  1264.  
  1265.             { Check if there is a pending free request that couldn't be honored because we were
  1266.             iterating and if so… be free! }
  1267.             IF fFreeRequested & (fTailNodePtr = NIL) THEN
  1268.                 Free;
  1269.  
  1270.             END;
  1271.     END;
  1272.  
  1273. {--------------------------------------------------------------------------------------------------}
  1274. {$S ListRes}
  1275.  
  1276. FUNCTION TSortedList.GetEqualItemNo(item: TObject): ArrayIndex; OVERRIDE;
  1277.  
  1278.     VAR
  1279.         index:                ArrayIndex;
  1280.  
  1281.         {$Push} {$IFC qTrace} {$D+} {$ENDC}
  1282.  
  1283.     FUNCTION TestItem(anItem: TObject): CompareResult;
  1284.  
  1285.         BEGIN
  1286.         IF qDebug THEN
  1287.             FailNonObject(anItem);
  1288.         TestItem := Compare(item, anItem);
  1289.         END;
  1290.     {$Pop}
  1291.  
  1292.     BEGIN
  1293.     IF qDebug THEN
  1294.         FailNonObject(item);
  1295.     IF DoSearch(TestItem, index) <> NIL THEN
  1296.         GetEqualItemNo := index
  1297.     ELSE
  1298.         GetEqualItemNo := kEmptyIndex;
  1299.     END;
  1300.  
  1301. {--------------------------------------------------------------------------------------------------}
  1302. {$S ListRes}
  1303.  
  1304. PROCEDURE TSortedList.Insert(item: TObject);
  1305.  
  1306.     VAR
  1307.         index:                ArrayIndex;
  1308.  
  1309.         {$Push} {$IFC qTrace} {$D+} {$ENDC}
  1310.  
  1311.     FUNCTION TestItem(anItem: TObject): CompareResult;
  1312.  
  1313.         BEGIN
  1314.         IF qDebug THEN
  1315.             FailNonObject(anItem);
  1316.         TestItem := Compare(item, anItem);
  1317.         END;
  1318.     {$Pop}
  1319.  
  1320.     BEGIN
  1321.     IF qDebug THEN
  1322.         FailNonObject(item);
  1323.  
  1324.     IF DoSearch(TestItem, index) <> NIL THEN;            { discard result }
  1325.     InsertBefore(index, item);
  1326.     END;
  1327.  
  1328. {--------------------------------------------------------------------------------------------------}
  1329. {$S ListRes}
  1330.  
  1331. FUNCTION TSortedList.Search(FUNCTION TestItem(anItem: TObject): CompareResult): TObject;
  1332.  
  1333.     VAR
  1334.         index:                ArrayIndex;
  1335.  
  1336.     BEGIN
  1337.     Search := DoSearch(TestItem, index);
  1338.     END;
  1339.  
  1340. {--------------------------------------------------------------------------------------------------}
  1341. {$S ListRes}
  1342.  
  1343. PROCEDURE TSortedList.Sort;
  1344.  
  1345.     {$Push} {$IFC qTrace} {$D+} {$ENDC}
  1346.     FUNCTION itsCompare(item1, item2: TObject): CompareResult;
  1347.  
  1348.         BEGIN
  1349.         itsCompare := Compare(item1, item2);
  1350.         END;
  1351.     {$Pop}
  1352.  
  1353.     BEGIN
  1354.     SortBy(itsCompare);
  1355.     END;
  1356.  
  1357. {--------------------------------------------------------------------------------------------------}
  1358. {$S MAFields}
  1359.  
  1360. PROCEDURE TSortedList.Fields(PROCEDURE DoToField(fieldName: Str255;
  1361.                                            fieldAddr: Ptr;
  1362.                                            fieldType: integer)); OVERRIDE;
  1363.  
  1364.     BEGIN
  1365.     DoToField('TSortedList', NIL, bClass);
  1366.  
  1367.     INHERITED Fields(DoToField);
  1368.     END;
  1369.  
  1370.  
  1371. {$IFC FALSE}                                            { Not supported in this release (2.0) }
  1372. {--------------------------------------------------------------------------------------------------}
  1373. {--------------------------------------------------------------------------------------------------}
  1374. {--------------------------------------------------------------------------------------------------}
  1375. {--------------------------------------------------------------------------------------------------}
  1376. (*
  1377. {$S ListRes}
  1378.  
  1379. PROCEDURE TList.Queue(item: TObject);
  1380.  
  1381.     BEGIN
  1382.     { Depend on InsertBefore for sanity checking }
  1383.     InsertLast(item);
  1384.     END;
  1385.  
  1386. {--------------------------------------------------------------------------------------------------}
  1387. {$S ListRes}
  1388.  
  1389. FUNCTION TList.Dequeue: TObject;
  1390.  
  1391.     BEGIN
  1392.     Dequeue := First;
  1393.     END;
  1394. *)
  1395.  
  1396. {--------------------------------------------------------------------------------------------------}
  1397. {$S ListRes}
  1398.  
  1399. PROCEDURE TLongintList.ILongintList;
  1400. { Initialize a new list with no elements, i.e., fSize = 0 }
  1401.  
  1402.     BEGIN
  1403.     IDynamicArray(kEmptyIndex, sizeof(longint));
  1404.     END;
  1405.  
  1406. {--------------------------------------------------------------------------------------------------}
  1407. {$S ListRes}
  1408.  
  1409. FUNCTION TLongintList.At(index: ArrayIndex): longint;
  1410. { Return the index'th element of the list. }
  1411.  
  1412.     BEGIN
  1413.     IF qRangeCheck & ((index < 1) | (index > fSize)) THEN
  1414.         BEGIN
  1415.         Writeln('fSize = ', fSize: 1, '  index = ', index: 1);
  1416.         ProgramBreak('Range Check in TLongintList.At');
  1417.         END;
  1418.  
  1419.     At := PLongint(ComputeAddress(index))^;
  1420.     END;
  1421.  
  1422. {--------------------------------------------------------------------------------------------------}
  1423. {$S ListRes}
  1424.  
  1425. PROCEDURE TLongintList.AtDelete(index: ArrayIndex);
  1426. { Deletes via an index. }
  1427.  
  1428.     BEGIN
  1429.     IF qRangeCheck & ((index < 1) | (index > fSize)) THEN
  1430.         BEGIN
  1431.         Writeln('fSize = ', fSize: 1, '  index = ', index: 1);
  1432.         ProgramBreak('Range Check in TLongintList.AtDelete');
  1433.         END;
  1434.  
  1435.     DeleteElementsAt(index, 1);
  1436.     END;
  1437.  
  1438. {--------------------------------------------------------------------------------------------------}
  1439. {$S ListRes}
  1440.  
  1441. PROCEDURE TLongintList.AtPut(index: ArrayIndex;
  1442.                              newItem: longint);
  1443.  { Replace the index'th element of the list.
  1444.    Range check only if the compile-flag qRangeCheck is TRUE. }
  1445.  
  1446.     BEGIN
  1447.     IF qRangeCheck & ((index < 1) | (index > fSize)) THEN
  1448.         BEGIN
  1449.         Writeln('fSize = ', fSize: 1, '  index = ', index: 1);
  1450.         ProgramBreak('Range Check in TLongintList.AtPut');
  1451.         END;
  1452.  
  1453.     PLongint(ComputeAddress(index))^ := newItem;
  1454.     END;
  1455.  
  1456. {--------------------------------------------------------------------------------------------------}
  1457. {$S ListRes}
  1458.  
  1459. PROCEDURE TLongintList.Delete(item: longint);
  1460.  
  1461.     VAR
  1462.         index:                ArrayIndex;
  1463.  
  1464.     BEGIN
  1465.     index := GetSameItemNo(item);
  1466.     IF index <> kEmptyIndex THEN
  1467.         AtDelete(index);
  1468.     END;
  1469.  
  1470. {--------------------------------------------------------------------------------------------------}
  1471. {$S ListRes}
  1472.  
  1473. PROCEDURE TLongintList.DeleteAll;
  1474.  
  1475.     BEGIN
  1476.     IF fSize > kEmptyIndex THEN
  1477.         DeleteElementsAt(1, fSize);
  1478.     END;
  1479.  
  1480. {--------------------------------------------------------------------------------------------------}
  1481. {$S ListRes}
  1482.  
  1483. PROCEDURE TLongintList.Each(PROCEDURE DoToItem(item: longint));
  1484.  
  1485.     VAR
  1486.         index:                ArrayIndex;
  1487.  
  1488.     FUNCTION TestItem(item: longint): Boolean;
  1489.  
  1490.         BEGIN
  1491.         DoToItem(item);
  1492.         TestItem := FALSE;
  1493.         END;
  1494.  
  1495.     BEGIN
  1496.     { just use IterateTil with a function that never returns true }
  1497.     IF IterateTil(TestItem, kIterateForward, index) <> 0 THEN;
  1498.     END;
  1499.  
  1500. {--------------------------------------------------------------------------------------------------}
  1501. {$S ListRes}
  1502.  
  1503. FUNCTION TLongintList.First: longint;
  1504.   { Return the first element of the list.
  1505.    If the compile-flag qDebug is TRUE & SetEltType was called, verify item's type. }
  1506.  
  1507.     BEGIN
  1508.     IF fSize <= kEmptyIndex THEN
  1509.         First := 0
  1510.     ELSE
  1511.         First := At(1);
  1512.     END;
  1513.  
  1514. {--------------------------------------------------------------------------------------------------}
  1515. {$S ListRes}
  1516.  
  1517. FUNCTION TLongintList.FirstThat(FUNCTION TestItem(item: longint): Boolean): longint;
  1518. { Call DoToItem once for each element of the list, in order. }
  1519.  
  1520.     VAR
  1521.         index:                ArrayIndex;
  1522.  
  1523.     BEGIN
  1524.     FirstThat := IterateTil(TestItem, kIterateForward, index);
  1525.     END;
  1526.  
  1527. {--------------------------------------------------------------------------------------------------}
  1528. {$S ListRes}
  1529.  
  1530. FUNCTION TLongintList.GetSameItemNo(item: longint): ArrayIndex;
  1531.  { Find the first reference to the IDENTITY item in the list.
  1532.   If item does not occur, return 0. }
  1533.  
  1534.     VAR
  1535.         index:                ArrayIndex;
  1536.  
  1537.     FUNCTION TestItem(listItem: longint): Boolean;
  1538.  
  1539.         BEGIN
  1540.         IF listItem = item THEN
  1541.             TestItem := TRUE
  1542.         ELSE
  1543.             TestItem := FALSE;
  1544.         END;
  1545.  
  1546.     BEGIN
  1547.     IF IterateTil(TestItem, kIterateForward, index) <> 0 THEN;
  1548.  
  1549.     GetSameItemNo := index;
  1550.     END;
  1551.  
  1552. {--------------------------------------------------------------------------------------------------}
  1553. {$S ListRes}
  1554.  
  1555. FUNCTION TLongintList.GetEqualItemNo(item: longint): ArrayIndex;
  1556.  { Find the first reference to item in the list.
  1557.   If item does not occur, return 0. }
  1558.  
  1559.     BEGIN
  1560.     GetEqualItemNo := GetSameItemNo(item);
  1561.     END;
  1562.  
  1563. {--------------------------------------------------------------------------------------------------}
  1564. {$S ListRes}
  1565.  
  1566. PROCEDURE TLongintList.InsertBefore(index: ArrayIndex;
  1567.                                     item: longint);
  1568.  { Insert a reference to item at the indicated index.
  1569.    If the compile-flag qDebug is TRUE & SetEltType was called, verify item's type. }
  1570.  
  1571.     BEGIN
  1572.     IF qRangeCheck & ((index < 1) | (index > fSize + 1)) THEN
  1573.         BEGIN
  1574.         Writeln('fSize = ', fSize: 1, '  index = ', index: 1);
  1575.         ProgramBreak('Range Check in TLongintList.InsertBefore');
  1576.         END;
  1577.  
  1578.     InsertElementsBefore(index, @item, 1);
  1579.     END;
  1580.  
  1581. {--------------------------------------------------------------------------------------------------}
  1582. {$S ListRes}
  1583.  
  1584. PROCEDURE TLongintList.InsertFirst(item: longint);
  1585.  { Insert a reference to item at the front of the list.
  1586.    If the compile-flag qDebug is TRUE & SetEltType was called, verify item's type. }
  1587.  
  1588.     BEGIN
  1589.     InsertBefore(1, item);
  1590.     END;
  1591.  
  1592. {--------------------------------------------------------------------------------------------------}
  1593.  
  1594. PROCEDURE TLongintList.InsertLast(item: longint);
  1595.  { Insert a reference to item at the back of the list.
  1596.    If qDebug is TRUE & SetEltType was called, check that item's type is correct. }
  1597.  
  1598.     BEGIN
  1599.     InsertBefore(fSize + 1, item);
  1600.     END;
  1601.  
  1602. {--------------------------------------------------------------------------------------------------}
  1603. {$S ListRes}
  1604.  
  1605. FUNCTION TLongintList.IterateTil(FUNCTION TestItem(item: longint): Boolean;
  1606.                                  IterateForward: Boolean;
  1607.                                  VAR itsIndex: ArrayIndex): longint;
  1608. { Call TestItem once for each element of the list, in order. }
  1609.  
  1610.     FUNCTION DoToElement(theIndex: ArrayIndex): Boolean;
  1611.  
  1612.         BEGIN
  1613.         DoToElement := TestItem(At(theIndex));
  1614.         END;
  1615.  
  1616.     BEGIN
  1617.     itsIndex := EachElementDoTil(DoToElement, IterateForward);
  1618.     IF itsIndex <> kEmptyIndex THEN
  1619.         IterateTil := At(itsIndex)
  1620.     ELSE
  1621.         IterateTil := 0;
  1622.     END;
  1623.  
  1624. {--------------------------------------------------------------------------------------------------}
  1625. {$S ListRes}
  1626.  
  1627. FUNCTION TLongintList.Last: longint;
  1628.  { Return the last element of the list.
  1629.    If the compile-flag qDebug is TRUE & SetEltType was called, verify item's type. }
  1630.  
  1631.     BEGIN
  1632.     IF fSize <= kEmptyIndex THEN
  1633.         Last := 0
  1634.     ELSE
  1635.         Last := At(fSize);
  1636.     END;
  1637.  
  1638. {--------------------------------------------------------------------------------------------------}
  1639. {$S ListRes}
  1640.  
  1641. FUNCTION TLongintList.LastThat(FUNCTION TestItem(item: longint): Boolean): longint;
  1642. { Call DoToItem once for each element of the list, in order. }
  1643.  
  1644.     VAR
  1645.         index:                ArrayIndex;
  1646.  
  1647.     BEGIN
  1648.     LastThat := IterateTil(TestItem, kIterateBackward, index);
  1649.     END;
  1650.  
  1651. {--------------------------------------------------------------------------------------------------}
  1652. {$S MAFields}
  1653.  
  1654. PROCEDURE TLongintList.Fields(PROCEDURE DoToField(fieldName: Str255;
  1655.                                                   fieldAddr: Ptr;
  1656.                                                   fieldType: integer)); OVERRIDE;
  1657.  
  1658.     BEGIN
  1659.     DoToField('TLongintList', NIL, bClass);
  1660.  
  1661.     INHERITED Fields(DoToField);
  1662.     END;
  1663.  
  1664. {--------------------------------------------------------------------------------------------------}
  1665. {$S MAFields}
  1666.  
  1667. PROCEDURE TLongintList.DynamicFields(PROCEDURE DoToField(fieldName: Str255;
  1668.                                                          fieldAddr: Ptr;
  1669.                                                          fieldType: integer)); OVERRIDE;
  1670.  
  1671.     VAR
  1672.         i:                    ArrayIndex;
  1673.         aString:            Str255;
  1674.  
  1675.     PROCEDURE ShowEntry(itsVal: longint);
  1676.  
  1677.         BEGIN
  1678.         i := i + 1;
  1679.         NumToString(i, aString);
  1680.         aString := CONCAT('.At[', aString, ']');
  1681.         DoToField(aString, @itsVal, bLongInt);
  1682.         END;
  1683.  
  1684.     BEGIN
  1685.     DoToField('Dynamic Fields', NIL, bTitle);
  1686.  
  1687.     i := kEmptyIndex;
  1688.     Each(ShowEntry);
  1689.     END;
  1690.  
  1691. {--------------------------------------------------------------------------------------------------}
  1692. {$S ListRes}
  1693.  
  1694. PROCEDURE TLongintList.Push(item: longint);
  1695. { LIFO stack push. }
  1696.  
  1697.     BEGIN
  1698.     InsertLast(item);
  1699.     END;
  1700.  
  1701. {--------------------------------------------------------------------------------------------------}
  1702. {$S ListRes}
  1703.  
  1704. FUNCTION TLongintList.Pop: longint;
  1705. { LIFO stack pop }
  1706.  
  1707.     BEGIN
  1708.     IF fSize = kEmptyIndex THEN
  1709.         Pop := 0
  1710.     ELSE
  1711.         BEGIN
  1712.         Pop := At(fSize);
  1713.         AtDelete(fSize);
  1714.         END;
  1715.     END;
  1716.  
  1717. {--------------------------------------------------------------------------------------------------}
  1718. {$S ListRes}
  1719.  
  1720. FUNCTION TSortedLongintList.Compare(item1, item2: longint): CompareResult;
  1721. { By default just compare the ordinal value of the items.  Subclasses that want to use the items
  1722. as pointers or other types should override if the comparison is based on the pointed at value }
  1723.  
  1724.     BEGIN
  1725.     IF item1 > item2 THEN
  1726.         Compare := kItem1GreaterThanItem2
  1727.     ELSE IF item1 < item2 THEN
  1728.         Compare := kItem1LessThanItem2
  1729.     ELSE
  1730.         Compare := kItem1EqualItem2;
  1731.     END;
  1732.  
  1733. {--------------------------------------------------------------------------------------------------}
  1734. {$S ListRes}
  1735.  
  1736. FUNCTION TSortedLongintList.DoSearch(FUNCTION TestItem(anItem: longint): CompareResult;
  1737.                                      VAR index: ArrayIndex): ArrayIndex;
  1738. { DON'T use EXIT to get out of this routine from your TestItem function or you will be really
  1739. sad!  (our debugger will check for you) That's why you can return TRUE to stop enumerating.
  1740. Signaling Failure is OK too. }
  1741.  
  1742.     VAR
  1743.         fi:                 FailInfo;
  1744.         myIterationNode:    IterationNode;
  1745.         aCompareResult:     CompareResult;
  1746.  
  1747.     PROCEDURE HdlDoSearch(error: OSErr;
  1748.                             message: longint);
  1749.  
  1750.         BEGIN
  1751.         RemoveNode(@myIterationNode);
  1752.         END;
  1753.  
  1754.     BEGIN
  1755.     DoSearch := 0;
  1756.  
  1757.     IF fSize = 0 THEN
  1758.         index := 1
  1759.     ELSE
  1760.         WITH myIterationNode DO                         { Make sure that the iterIndex counter and
  1761.                                                          Iteration direction flag from
  1762.                                                          myIterationNode are used so that the
  1763.                                                          iterIndex counter can be "bent" if anyone
  1764.                                                          else deletes or inserts elements while the
  1765.                                                          iteration is in progress. Pretty slick,
  1766.                                                          eh? }
  1767.             BEGIN
  1768.             AppendNode(@myIterationNode);                { link me in to the list of iterations in
  1769.                                                          progress }
  1770.             CatchFailures(fi, HdlDoSearch);
  1771.  
  1772.             iterForward := IterateForward;
  1773.             iterLowBound := 1;
  1774.             iterHighBound := fSize;
  1775.             REPEAT
  1776.                 iterIndex := BSR(iterLowBound + iterHighBound, 1); { (iterLowBound + iterHighBound)
  1777.                                                                     DIV 2 }
  1778.                 aCompareResult := TestItem(At(iterIndex));
  1779.                 IF aCompareResult <= kItemGreaterThanCriteria THEN
  1780.                     iterHighBound := iterIndex - 1
  1781.                 ELSE
  1782.                     iterLowBound := iterIndex + 1;
  1783.             UNTIL (aCompareResult = kItemEqualCriteria) | (iterLowBound > iterHighBound);
  1784.             IF aCompareResult = kItemEqualCriteria THEN
  1785.                 DoSearch := At(iterIndex)
  1786.             ELSE IF aCompareResult >= kItemLessThanCriteria THEN
  1787.                 iterIndex := iterIndex + 1;
  1788.  
  1789.             { keep index in range }
  1790.             IF (iterIndex < 1) | (iterIndex > fSize + 1) THEN
  1791.                 index := kEmptyIndex
  1792.             ELSE
  1793.                 index := iterIndex;
  1794.  
  1795.             Success(fi);
  1796.             RemoveNode(@myIterationNode);
  1797.  
  1798.             { Check if there is a pending free request that couldn't be honored because we were
  1799.             iterating and if so… be free! }
  1800.             IF fFreeRequested & (fTailNodePtr = NIL) THEN
  1801.                 Free;
  1802.  
  1803.             END;
  1804.     END;
  1805.  
  1806. {--------------------------------------------------------------------------------------------------}
  1807. {$S ListRes}
  1808.  
  1809. FUNCTION TSortedLongintList.GetEqualItemNo(item: longint): ArrayIndex; OVERRIDE;
  1810.  
  1811.     VAR
  1812.         index:                ArrayIndex;
  1813.  
  1814.         {$Push} {$IFC qTrace} {$D+} {$ENDC}
  1815.  
  1816.     FUNCTION TestItem(anItem: longint): CompareResult;
  1817.  
  1818.         BEGIN
  1819.         TestItem := Compare(item, anItem);
  1820.         END;
  1821.     {$Pop}
  1822.  
  1823.     BEGIN
  1824.     IF DoSearch(TestItem, index) <> 0 THEN
  1825.         GetEqualItemNo := index
  1826.     ELSE
  1827.         GetEqualItemNo := kEmptyIndex;
  1828.     END;
  1829.  
  1830. {--------------------------------------------------------------------------------------------------}
  1831. {$S ListRes}
  1832.  
  1833. PROCEDURE TSortedLongintList.Insert(item: longint);
  1834.  
  1835.     VAR
  1836.         index:                ArrayIndex;
  1837.  
  1838.         {$Push} {$IFC qTrace} {$D+} {$ENDC}
  1839.  
  1840.     FUNCTION TestItem(anItem: longint): CompareResult;
  1841.  
  1842.         BEGIN
  1843.         TestItem := Compare(item, anItem);
  1844.         END;
  1845.     {$Pop}
  1846.  
  1847.     BEGIN
  1848.     IF DoSearch(TestItem, index) <> 0 THEN;
  1849.     InsertBefore(index, item);
  1850.     END;
  1851.  
  1852. {--------------------------------------------------------------------------------------------------}
  1853. {$S ListRes}
  1854.  
  1855. FUNCTION TSortedLongintList.Search(FUNCTION TestItem(anItem: longint): CompareResult): longint;
  1856.  
  1857.     VAR
  1858.         index:                integer;
  1859.  
  1860.     BEGIN
  1861.     Search := DoSearch(TestItem, index);
  1862.     END;
  1863.  
  1864. {$EndC}
  1865.